`include "defines.v"

module RVCPU (
    input wire clk,
    input wire rst,
    output wire load_use_stall,
    output wire flush,

    input  wire [31: 0] inst_mem_data,
    output wire         inst_ena,
    output wire [63: 0] inst_mem_addr,

    input  wire [`xlen] ram_rd_data,
    output wire [63: 0] ram_addr,

    output wire [`xlen] ram_wr_data,
    output wire         ram_rd_en,
    output wire         ram_wr_en,
    output wire [`BUSLEN] ram_wmask,

    //for sim
    output wire [63: 0] gpr [31: 0],
    output wire RegWr,
    output wire [4:0] rd_addr,
    output wire [`xlen] data_to_reg,
    output wire [63: 0] pc_addr,
    output wire [`INSTLEN] inst
);
//wire load_use_stall;
//wire flush;

//regfile
wire         rs1_ena;
wire [ 4: 0] rs1_addr;
wire [`xlen] rs1_data;
wire         rs2_ena;
wire [ 4: 0] rs2_addr;
wire [`xlen] rs2_data;
//wire         RegWr;
//wire [ 4: 0] rd_addr;  sim
//wire [`xlen] data_to_reg;

regfile regfile (
    .clk(clk),
    .rst(rst),
    
    .rena1(rs1_ena),
    .raddr1(rs1_addr),
    .rdata1(rs1_data),

    .rena2(rs2_ena),
    .raddr2(rs2_addr),
    .rdata2(rs2_data),

    .we(RegWr),
    .waddr(rd_addr),
    .wdata(data_to_reg),
    .rf(gpr)
);
//

//CSR
wire         csr_ena;
wire [11: 0] csr_addr;
wire [`xlen] csr_data;

wire         csr_wen;
wire [11: 0] csr_waddr;
wire [`xlen] csr_wdata;

CSR CSR(
    .clk(clk),
    .rst(rst),

    .csr_ena(csr_ena),
    .csr_addr(csr_addr),
    .csr_data(csr_data),

    .csr_wen(csr_wen),
    .csr_waddr(csr_waddr),
    .csr_wdata(csr_wdata)
);
//

//IF
wire [63: 0] pc_if;
wire [31: 0] instruction;
wire         pc_sel;
wire [63: 0] pc_addr_calc;

IF_Stage Instruction_Fetch (
    .clk(clk),
    .rst(rst),
    .load_use_stall(load_use_stall),
    .pc_sel(pc_sel),
    .pc_jorb(pc_addr_calc),
    .im_data_i(inst_mem_data),
    .inst_ena(inst_ena),
    .im_data_o(instruction),
    .pc(pc_if)
);

assign inst_mem_addr = pc_if;

//pipeline reg IF->ID
//[95:32]:pc_addr;    [31:0]:instruction;
reg [95:0] IF_to_ID;
always @(posedge clk) begin
    if (rst | flush) IF_to_ID <= 96'h13;
    else if(load_use_stall) begin
        IF_to_ID <= IF_to_ID;
    end
    else begin
        IF_to_ID[95:32] <= pc_if;
        IF_to_ID[31: 0] <= instruction;
    end
end
//
//sim
assign pc_addr = IF_to_ID[95:32];
assign inst = IF_to_ID[31: 0];

//ID
wire [`xlen] regbusA;
wire         rs1_data_sign;
wire [`xlen] regbusB;
wire [`xlen] imm;
wire [63: 0] pc_id;
wire [ 4: 0] rd_addr_id;

wire [ 1: 0] ALU_oprendA_src;
wire [ 1: 0] ALU_oprendB_src;
wire [ 2: 0] ALU_op;
wire [ 2: 0] ALU_out_ext_type_id;

wire [ 3: 0] MemWr_id;
wire         MemToReg_id;
wire         RegWr_id;
wire [ 2: 0] load_ext_type_id;

wire         adder_src;
wire [ 2: 0] branch_id;
wire         jump_id;
wire         is_system_inst_id;

wire         ForwardA;
wire         ForwardB;
wire         ForwardCSR;
wire [`xlen] forward_rs1_data;
wire [`xlen] forward_rs2_data;
wire [`xlen] forward_csr_data;

ID_Stage Instruction_Decode (
    .pc_i(IF_to_ID[95:32]),
    .inst(IF_to_ID[31: 0]),

    .ForwardA(ForwardA),
    .ForwardB(ForwardB),
    .ForwardCSR(ForwardCSR),
    .forward_rs1_data(forward_rs1_data),
    .forward_rs2_data(forward_rs2_data),
    .forward_csr_data(forward_csr_data),

    .pc_o(pc_id),

    .rs1_data_i(rs1_data),
    .rs2_data_i(rs2_data),
    .csr_data_i(csr_data),
    .regbusA(regbusA),
    .rs1_data_sign(rs1_data_sign),
    .regbusB(regbusB),

    .imm(imm),

    .rs1_ena(rs1_ena),
    .rs1_raddr(rs1_addr),
    .rs2_ena(rs2_ena),
    .rs2_raddr(rs2_addr),

    .rd_addr(rd_addr_id),

    .csr_ena(csr_ena),
    .csr_addr(csr_addr),

    .ALU_oprendA_src(ALU_oprendA_src),
    .ALU_oprendB_src(ALU_oprendB_src),
    .ALU_op(ALU_op),
    .ALU_out_ext_type(ALU_out_ext_type_id),

    .MemWr(MemWr_id),
    .MemToReg(MemToReg_id),
    .RegWr(RegWr_id),
    .load_ext_type(load_ext_type_id),


    .adder_src(adder_src),
    .branch(branch_id),
    .jump(jump_id),

    .is_system_inst(is_system_inst_id)
);

//pipeline reg ID->EX
//[298:287]csr_addr;    [286]is_system_inst;      [285]:rs1_data_sign;   [284:221]:pc_addr;   [220:157]:regbusA;   [156:93]:regbusB;   [92:29]:imm;     [28:24]:rd_addr;
//[23:22]:ALU_oprendA_src;      [21:20]:ALU_oprendB_src;      [19:17]:ALU_op;     [16]:adder_src;
//[15]:jump;             [14:12]:branch;     [11:8]:MemWr;    [7]:MemToReg;    [6]:RegWr;    [5:3]:load_ext_type;   [2:0]:ALU_out_ext_type ;
reg [298: 0] ID_to_EX;

always @(posedge clk) begin
    if (rst | load_use_stall | flush) ID_to_EX <= 299'h2000;
    else begin
        ID_to_EX[298:287] <= csr_addr;
        ID_to_EX[    286] <= is_system_inst_id;
        ID_to_EX[    285] <= rs1_data_sign;
        ID_to_EX[284:221] <= pc_id;
        ID_to_EX[220:157] <= regbusA;
        ID_to_EX[156: 93] <= regbusB;
        ID_to_EX[ 92: 29] <= imm;
        ID_to_EX[ 28: 24] <= rd_addr_id;
        ID_to_EX[ 23: 22] <= ALU_oprendA_src;
        ID_to_EX[ 21: 20] <= ALU_oprendB_src;
        ID_to_EX[ 19: 17] <= ALU_op;
        ID_to_EX[     16] <= adder_src;
        ID_to_EX[     15] <= jump_id;
        ID_to_EX[ 14: 12] <= branch_id;
        ID_to_EX[ 11:  8] <= MemWr_id;
        ID_to_EX[      7] <= MemToReg_id;
        ID_to_EX[      6] <= RegWr_id;
        ID_to_EX[  5:  3] <= load_ext_type_id;
        ID_to_EX[   2: 0] <= ALU_out_ext_type_id;
    end
end
//


//EX

wire [63: 0] pc_ex;
wire [ 4: 0] rd_addr_ex;
wire [11: 0] csr_addr_ex;

wire [`xlen] ALU_out_ex;
wire [`xlen] data_to_mem_ex;
wire [ 2: 0] branch_ex;
wire         jump_ex;


wire [ 3: 0] MemWr_ex;
wire         MemToReg_ex;
wire         RegWr_ex;
wire [ 2: 0] load_ext_type_ex;
wire         is_system_inst_ex;

wire [ 3: 0] flags;

EX_Stage Execute (
    .pc_i(ID_to_EX[284:221]),
    .rd_addr_i(ID_to_EX[ 28: 24]),
    .csr_addr_i(ID_to_EX[298:287]),
    .pc_o(pc_ex),
    .rd_addr_o(rd_addr_ex),
    .csr_addr_o(csr_addr_ex),

    .regbusA(ID_to_EX[220:157]),
    .rs1_data_sign(ID_to_EX[285]),
    .regbusB(ID_to_EX[156: 93]),
    .imm(ID_to_EX[ 92: 29]),
    .ALU_out(ALU_out_ex),
    .data_to_mem(data_to_mem_ex),

    .branch_i(ID_to_EX[ 14: 12]),
    .jump_i(ID_to_EX[15] ),
    .adder_oprendA_src(ID_to_EX[16]),
    .is_system_inst_i(ID_to_EX[286]),
    .is_system_inst_o(is_system_inst_ex),
    .branch_o(branch_ex),
    .jump_o(jump_ex),

    .ALU_oprendA_src(ID_to_EX[ 23: 22]),
    .ALU_oprendB_src(ID_to_EX[ 21: 20]),
    .ALU_op(ID_to_EX[ 19: 17]),
    .ALU_out_ext_type(ID_to_EX[2:0]),

    .MemWr_i(ID_to_EX[ 11:  8]),
    .MemToReg_i(ID_to_EX[7]),
    .RegWr_i(ID_to_EX[6]),
    .load_ext_type_i(ID_to_EX[ 5: 3]),
    .MemWr_o(MemWr_ex),
    .MemToReg_o(MemToReg_ex),
    .RegWr_o(RegWr_ex),
    .load_ext_type_o(load_ext_type_ex),

    .flags(flags)
);

//pipeline reg EX->MEM
//[227:216]:csr_addr;   [215]:is_system_inst;     [214:151]:pc_addr;     [150:87]:data_to_mem;   [86:23]:ALU_out;     [22:18]:rd_addr;
//[17:14]:flags;        [13]:jump;        [12:10]:branch;
//[9:6]:MemWr;    [5]:MemToReg;    [4]:RegWr;    [3:1]:load_ext_type;   [0]:ALU_out_ext_type ;
reg [227:0] EX_to_MEM;

always @(posedge clk) begin
    if (rst | flush) EX_to_MEM <= 228'h800;
    else begin
        EX_to_MEM[227:216] <= csr_addr;
        EX_to_MEM[    215] <= is_system_inst_ex;
        EX_to_MEM[214:151] <= pc_ex;
        EX_to_MEM[150: 87] <= data_to_mem_ex;
        EX_to_MEM[ 86: 23] <= ALU_out_ex;
        EX_to_MEM[ 22: 18] <= rd_addr_ex;
        EX_to_MEM[ 17: 14] <= flags;
        EX_to_MEM[     13] <= jump_ex;
        EX_to_MEM[ 12: 10] <= branch_ex;
        EX_to_MEM[  9:  6] <= MemWr_ex;
        EX_to_MEM[      5] <= MemToReg_ex;
        EX_to_MEM[      4] <= RegWr_ex;
        EX_to_MEM[  3:  1] <= load_ext_type_ex;
        EX_to_MEM[      0] <= 1'b0;//need to delete
    end
end

//



//MEM
wire [ 4: 0] rd_addr_mem;
wire [11: 0] csr_addr_mem;
wire [`xlen] data_from_mem;
wire         RegWr_mem;
wire         MemToReg_mem;
wire [ 2: 0] load_ext_type_mem;
wire         is_system_inst_mem;
wire [ 7: 0] mem_byte_ena;
wire MemRd;
wire MemWr_ena;
wire [63: 0] data_mem_addr;
wire [`BUSLEN] data_to_mem;
wire [`BUSLEN] data_mem_data;

MEM_Stage Memory (
    .pc_addr_i(EX_to_MEM[214:151]),
    .pc_addr_o(pc_addr_calc),

    .ALU_out_i(EX_to_MEM[ 86: 23]),
    .ALU_out_o(data_mem_addr),

    .rd_addr_i(EX_to_MEM[ 22: 18]),
    .rd_addr_o(rd_addr_mem),

    .csr_addr_i(EX_to_MEM[227:216]),
    .csr_addr_o(csr_addr_mem),

    .data_to_mem_i(EX_to_MEM[150: 87]),
    .data_from_mem_i(data_from_mem),
    .data_to_mem_o(data_to_mem),
    .data_from_mem_o(data_mem_data),

    .jump(EX_to_MEM[13]),
    .branch(EX_to_MEM[ 12: 10]),
    .flags(EX_to_MEM[ 17: 14]),
    .pcsel(pc_sel),

    .is_system_inst_i(EX_to_MEM[215]),
    .MemWr_i(EX_to_MEM[ 9: 6]),
    .RegWr_i(EX_to_MEM[4]),
    .MemToReg_i(EX_to_MEM[5]),
    .MemRd(MemRd),
    .load_ext_i(EX_to_MEM[  3:  1]),
    .is_system_inst_o(is_system_inst_mem),
    .MemWr_ena(MemWr_ena),
    .mem_byte_ena(mem_byte_ena),
    .RegWr_o(RegWr_mem),
    .MemToReg_o(MemToReg_mem),
    .load_ext_o(load_ext_type_mem)
);


mem_ctrl mem_ctrl(
    .rst(rst),

    .mem_byte_enble(mem_byte_ena),
    .mem_addr(data_mem_addr),
    .mem_rd_en(MemRd),
    .mem_wr_en(MemWr_ena),
    .mem_wr_data(data_to_mem),
    .mem_rd_data(data_from_mem),

    .ram_addr(ram_addr),
    .ram_wr_en(ram_wr_en),
    .ram_wmask(ram_wmask),
    .ram_wr_data(ram_wr_data),
    .ram_rd_en(ram_rd_en),
    .ram_rd_data(ram_rd_data)
);  

//pipeline reg MEM->WB
//[151:140]csr_addr;    [139]:is_system_inst;     [138:75]:data_from_mem;   [74:11]:ALU_out;     [10:6]:rd_addr;
//[5]:MemToReg;    [4]:RegWr;    [3:1]:load_ext_type;   [0]:ALU_out_ext_type ;
reg [151:0] MEM_to_WB;
always @(posedge clk) begin
    if (rst) MEM_to_WB <= 152'b0;
    else begin
        MEM_to_WB[151:140] <= csr_addr_mem;
        MEM_to_WB[    139] <= is_system_inst_mem;
        MEM_to_WB[138: 75] <= data_mem_data;
        MEM_to_WB[ 74: 11] <= EX_to_MEM[ 86: 23];
        MEM_to_WB[ 10:  6] <= rd_addr_mem;
        MEM_to_WB[      5] <= MemToReg_mem;
        MEM_to_WB[      4] <= RegWr_mem;
        MEM_to_WB[  3:  1] <= load_ext_type_mem;
        MEM_to_WB[      0] <= 1'b0;  //need to delete
    end
end
//

//WB
WB_Stage Write_Back (
    .rd_addr_i(MEM_to_WB[ 10:  6]),
    .rd_addr_o(rd_addr),

    .is_system_inst(MEM_to_WB[139]),
    .csr_addr_i(MEM_to_WB[151:140]),
    .csr_wen(csr_wen),
    .csr_addr_o(csr_waddr),

    .data_from_mem(MEM_to_WB[138: 75]),
    .ALU_out(MEM_to_WB[ 74: 11]),
    .data_to_reg(data_to_reg),
    .csr_data(csr_data),

    .MemToReg(MEM_to_WB[ 5]),
    .RegWr_i(MEM_to_WB[ 4]),
    .load_ext_type(MEM_to_WB[  3:  1]),
    .RegWr_o(RegWr)
);
//

//
Hazard_Detection Hazard_Detection(
    .rs1_addr(rs1_addr),
    .rs2_addr(rs2_addr),
    .rd_addr_ex(rd_addr_ex),
    .rd_addr_mem(rd_addr_mem),
    .MemToReg_ex(MemToReg_ex),
    .MemToReg_mem(MemToReg_mem),
    .load_use_stall(load_use_stall),

    .pc_sel(pc_sel),
    .flush(flush)
);

//
wire [`xlen] csr_data_ex;
wire [`xlen] csr_data_mem;
wire [`xlen] csr_data_wb;

assign csr_data_ex = ALU_out_ex;
assign csr_data_mem = data_mem_addr;
assign csr_data_wb = csr_wdata;

Forwarding Forwarding(
    .rs1_addr(rs1_addr),
    .rs2_addr(rs2_addr),
    .csr_addr(csr_addr),

    .rd_addr_ex(rd_addr_ex),
    .rd_addr_mem(rd_addr_mem),
    .rd_addr_wb(MEM_to_WB[ 10:  6]),
    .csr_addr_ex(),
    .csr_addr_mem(),
    .csr_addr_wb(),

    .RegWr_ex(RegWr_ex),
    .RegWr_mem(RegWr_mem),
    .RegWr_wb(RegWr),
    .MemToReg_ex(MemToReg_ex),
    .MemToReg_mem(MemToReg_mem),
    .MemToReg_wb(MEM_to_WB[ 5]),
    .is_system_inst_ex(is_system_inst_ex),
    .is_system_inst_mem(is_system_inst_mem),
    .is_system_inst_wb(csr_wen),

    .ForwardA(ForwardA),
    .ForwardB(ForwardB),
    .ForwardCSR(ForwardCSR),

    .rd_data_ex(ALU_out_ex),
    .rd_data_mem(data_mem_addr),
    .rd_data_wb(data_to_reg),
    .csr_data_ex(csr_data_ex),
    .csr_data_mem(csr_data_mem),
    .csr_data_wb(csr_data_wb),

    .forward_rs1_data(forward_rs1_data),
    .forward_rs2_data(forward_rs2_data),
    .forward_csr_data(forward_csr_data)
);

endmodule